home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-01 / ue312src.zip / INPUT.C < prev    next >
C/C++ Source or Header  |  1993-04-21  |  29KB  |  1,255 lines

  1. /*    Input:    Various input routines for MicroEMACS
  2.         written by Daniel Lawrence
  3.         copyright 1986-1993 by Daniel M. Lawrence
  4.  
  5.     Notes:
  6.  
  7.     MicroEMACS's kernel processes two distinct forms of
  8.     characters.  One of these is a standard unsigned character
  9.     which is used in the edited text.  The other form, called
  10.     an EMACS Extended Character is a 2 byte value which contains
  11.     both an ascii value, and flags for certain prefixes/events.
  12.  
  13.     Bit    Usage
  14.     ---    -----
  15.     0 -> 7    Standard 8 bit ascii character
  16.     8    Control key flag
  17.     9    META prefix flag
  18.     10    ^X prefix flag
  19.     11    Function key flag
  20.     12    Mouse prefix
  21.     13    Shifted flag (not needed on alpha shifted characters)
  22.     14    Alterate prefix (ALT key on PCs)
  23.  
  24.     The machine dependent driver is responsible for returning
  25.     a byte stream from the various input devices with various
  26.     prefixes/events embedded as escape codes.  Zero is used as the
  27.     value indicating an escape sequence is next.  The format of
  28.     an escape sequence is as follows:
  29.  
  30.     0        Escape indicator
  31.     <prefix byte>    upper byte of extended character
  32.     {<col><row>}    col, row position if the prefix byte
  33.             indicated a mouse event or a menu selection
  34.             in which case these form a 16 bit menu ID
  35.     <event code>    value of event
  36.  
  37.     A ^<space> sequence (0/1/32) is generated when an actual
  38.     null is being input from the control-space key under many
  39.     unix systems.  These values are then interpreted by getkey()
  40.     to construct the proper extended character sequences to pass
  41.     to the MicroEMACS kernel.
  42. */
  43.  
  44. #include    <stdio.h>
  45. #include    "estruct.h"
  46. #include    "eproto.h"
  47. #include    "edef.h"
  48. #include    "elang.h"
  49.  
  50. #if USG | AUX | BSD | V7 | SUN | HPUX8 | HPUX9
  51. #include    <pwd.h>
  52. extern struct passwd *getpwnam();
  53. #endif
  54.  
  55. /*
  56.  * Ask a yes or no question in the message line. Return either TRUE, FALSE, or
  57.  * ABORT. The ABORT status is returned if the user bumps out of the question
  58.  * with a ^G. Used any time a confirmation is required.
  59.  */
  60.  
  61. #if    !WINDOW_MSWIN    /* for MS Windows, mlyesno is defined in mswsys.c */
  62. PASCAL NEAR mlyesno(prompt)
  63.  
  64. char *prompt;
  65.  
  66. {
  67.     int  c;            /* input character */
  68.     char buf[NPAT];        /* prompt to user */
  69.  
  70.     for (;;) {
  71.         /* build and prompt the user */
  72.         strcpy(buf, prompt);
  73.         strcat(buf, TEXT162);
  74. /*                          " [y/n]? " */
  75.         mlwrite(buf);
  76.  
  77.         /* get the response */
  78.             c = getcmd();   /* getcmd() lets us check for anything that might */
  79.                             /* generate a 'y' or 'Y' in case use screws up */
  80.  
  81.         if (c == ectoc(abortc))        /* Bail out! */
  82.             return(ABORT);
  83.  
  84.             if  ((c == 'n') || (c == 'N')
  85.                 || (c & (SPEC|ALTD|CTRL|META|CTLX|MOUS)))
  86.                     return(FALSE);  /* ONLY 'y' or 'Y' allowed!!! */
  87.  
  88. #if    FRENCH
  89.         if (c=='o' || c=='O')
  90.             return(TRUE);
  91. #endif
  92.  
  93.         if (c=='y' || c=='Y')
  94.             return(TRUE);
  95.  
  96.         return(FALSE);
  97.     }
  98. }
  99. #endif
  100.  
  101. /*
  102.  * Write a prompt into the message line, then read back a response. Keep
  103.  * track of the physical position of the cursor. If we are in a keyboard
  104.  * macro throw the prompt away, and return the remembered response. This
  105.  * lets macros run at full speed. The reply is always terminated by a carriage
  106.  * return. Handle erase, kill, and abort keys.
  107.  */
  108.  
  109. PASCAL NEAR mlreply(prompt, buf, nbuf)
  110.  
  111. char *prompt;
  112. char *buf;
  113. int nbuf;
  114.  
  115. {
  116.     return(nextarg(prompt, buf, nbuf, ctoec((int) '\r')));
  117. }
  118.  
  119. /*    ectoc:    expanded character to character
  120.         collapse the CTRL and SPEC flags back into an ascii code   */
  121.  
  122. PASCAL NEAR ectoc(c)
  123.  
  124. int c;
  125.  
  126. {
  127.     if (c == (CTRL | ' '))
  128.         c = 0;
  129.     if (c & CTRL)
  130.         c = c ^ (CTRL | 0x40);
  131.     if (c & SPEC)
  132.         c = c & 255;
  133.     return(c);
  134. }
  135.  
  136. /*    ctoec:    character to extended character
  137.         pull out the CTRL and SPEC prefixes (if possible)    */
  138.  
  139. PASCAL NEAR ctoec(c)
  140.  
  141. int c;
  142.  
  143. {
  144.     if (c == 0)
  145.         c = CTRL | ' ';
  146.     else if ((c >= 0x00 && c <= 0x1F) || c == 0x7F)
  147.                 c = CTRL | (c ^ 0x40);
  148.         return(c);
  149. }
  150.  
  151. /* get a command name from the command line. Command completion means
  152.    that pressing a <SPACE> will attempt to complete an unfinished command
  153.    name if it is unique.
  154. */
  155.  
  156. #if    MSC
  157. int (PASCAL NEAR *PASCAL NEAR getname(char *prompt))(void)
  158. #else
  159. int (PASCAL NEAR *PASCAL NEAR getname(prompt))()
  160.  
  161. char *prompt;    /* string to prompt with */
  162. #endif
  163.  
  164. {
  165.     char *sp;    /* ptr to the returned string */
  166.  
  167.     sp = complete(prompt, NULL, CMP_COMMAND, NSTRING);
  168.     if (sp == NULL)
  169.         return(NULL);
  170.  
  171.     return(fncmatch(sp));
  172. }
  173.  
  174. /*    getcbuf:    get a completion from the user for a buffer name.
  175.  
  176.             I was goaded into this by lots of other people's
  177.             completion code.
  178. */
  179.  
  180. BUFFER *PASCAL NEAR getcbuf(prompt, defval, createflag)
  181.  
  182. char *prompt;        /* prompt to user on command line */
  183. char *defval;        /* default value to display to user */
  184. int createflag;        /* should this create a new buffer? */
  185.  
  186. {
  187.     char *sp;    /* ptr to the returned string */
  188.  
  189.     sp = complete(prompt, defval, CMP_BUFFER, NBUFN);
  190.     if (sp == NULL)
  191.         return(NULL);
  192.  
  193.     return(bfind(sp, createflag, 0));
  194. }
  195.  
  196. char *PASCAL NEAR gtfilename(prompt)
  197.  
  198. char *prompt;        /* prompt to user on command line */
  199.  
  200. {
  201. #if    MSDOS | OS2
  202.     char *scan;
  203. #endif
  204. #if    WINDOW_MSWIN
  205.     static char sp[NFILEN];
  206.  
  207.     if (!FILENAMEREPLY(prompt, sp, NFILEN))
  208.         return NULL;
  209. #else
  210.     char *sp;    /* ptr to the returned string */
  211.  
  212.     /* get a file name, default to current buffer's */
  213.     if (curbp && curbp->b_fname && strcmp(curbp->b_fname, "") != 0)
  214.         sp = complete(prompt, curbp->b_fname, CMP_FILENAME, NFILEN);
  215.     else
  216.         sp = complete(prompt, NULL, CMP_FILENAME, NFILEN);
  217. #endif
  218. #if    MSDOS | OS2
  219.     /* change forward slashes to back */
  220.     if (sp) {
  221.         scan = sp;
  222.         while (*scan) {
  223.             if (*scan == '/')
  224.                 *scan = DIRSEPCHAR;
  225.             ++scan;
  226.         }
  227.     }
  228. #endif
  229.     return(sp);
  230. }
  231.  
  232. char *PASCAL NEAR complete(prompt, defval, type, maxlen)
  233.  
  234. char *prompt;        /* prompt to user on command line */
  235. char *defval;        /* default value to display to user */
  236. int type;        /* type of what we are completing */
  237. int maxlen;        /* maximum length of input field */
  238.  
  239. {
  240.     register int c;        /* current input character */
  241.     register int ec;    /* extended input character */
  242.     int cpos;        /* current column on screen output */
  243.     char *home_ptr;        /* pointer to home directory string */
  244.     char *ptr;        /* string pointer */
  245.     char user_name[NSTRING]; /* user name for directory */
  246.     static char buf[NSTRING];/* buffer to hold tentative name */
  247. #if USG | AUX | BSD | V7 | SUN | HPUX8 | HPUX9
  248.     struct passwd *pwd;    /* password structure */
  249. #endif
  250. #if    ENVFUNC
  251.     char *getenv();         /* get environment string */
  252. #endif
  253.  
  254.     /* if we are executing a command line get the next arg and match it */
  255.     if (clexec) {
  256.         if (macarg(buf) != TRUE)
  257.             return(NULL);
  258.         return(buf);
  259.     }
  260.  
  261.     /* starting at the beginning of the string buffer */
  262.     cpos = 0;
  263.  
  264.     /* if it exists, prompt the user for a buffer name */
  265.     if (prompt)
  266.         if (type == CMP_COMMAND)
  267.             mlwrite("%s", prompt);
  268.         else if (defval)
  269.             mlwrite("%s[%s]: ", prompt, defval);
  270.         else
  271.             mlwrite("%s: ", prompt);
  272.  
  273.     /* build a name string from the keyboard */
  274.     while (TRUE) {
  275.  
  276.         /* get the keystroke and decode it */
  277.         ec = getkey();
  278.         c = ectoc(ec);        
  279.  
  280.         /* if it is from the mouse, or is a function key, blow it off */
  281.         if ((ec & MOUS) || (ec & SPEC))
  282.             continue;
  283.  
  284.         /* if we are at the end, just match it */
  285.         if (c == '\n'  ||  c == '\r') {
  286.             if (defval && cpos==0)
  287.                 return(defval);
  288.             else {
  289.                 buf[cpos] = 0;
  290.                 return(buf);
  291.             }
  292.  
  293.         } else if (ec == abortc) {    /* Bell, abort */
  294.             ctrlg(FALSE, 0);
  295.             TTflush();
  296.             return(NULL);
  297.  
  298.         } else if (c == 0x7F || c == 0x08) {    /* rubout/erase */
  299.             if (cpos != 0) {
  300.                 mlout('\b');
  301.                 mlout(' ');
  302.                 mlout('\b');
  303.                 --ttcol;
  304.                 --cpos;
  305.                 TTflush();
  306.             }
  307.  
  308.         } else if (c == 0x15) {    /* C-U, kill */
  309.             while (cpos != 0) {
  310.                 mlout('\b');
  311.                 mlout(' ');
  312.                 mlout('\b');
  313.                 --cpos;
  314.                 --ttcol;
  315.             }
  316.             TTflush();
  317.  
  318.         } else if ((c == ' ') || (ec == sterm) || (c == '\t')) {    
  319.             /* attempt a completion */
  320.             switch (type) {
  321.                 case CMP_BUFFER:
  322.                     comp_buffer(buf, &cpos);
  323.                     break;
  324.                 case CMP_COMMAND:
  325.                     comp_command(buf, &cpos);
  326.                     break;
  327. #if    !WINDOW_MSWIN
  328.                 case CMP_FILENAME:
  329.                     comp_file(buf, &cpos);
  330.                     break;
  331. #endif
  332.             }
  333.  
  334.             TTflush();
  335.             if (buf[cpos - 1] == 0)
  336.                 return(buf);
  337.             goto clist;
  338.  
  339. #if    ENVFUNC
  340.         } else if ((cpos > 0) &&
  341.                ((char)c == DIRSEPCHAR) &&
  342.                (type == CMP_FILENAME) &&
  343.                (buf[0] == '~') &&
  344.                ((home_ptr = getenv("HOME")) != (char *)NULL)) {
  345.  
  346.             /* save the user name! */
  347.             buf[cpos] = 0;
  348.             strcpy(user_name, &buf[1]);
  349.  
  350.             /* erase the chars on-screen */
  351.             while (cpos > 0) {
  352.                 mlout('\b');
  353.                 mlout(' ');
  354.                 mlout('\b');
  355.                 --cpos;
  356.                 --ttcol;
  357.             }
  358.  
  359. #if USG | AUX | BSD | V7 | SUN | HPUX8 | HPUX9
  360.             /* lookup someone else's home directory! */
  361.             if (user_name[0] != 0) {
  362.                 pwd = getpwnam(user_name);
  363.                 if (pwd != (struct passwd *)NULL) {
  364.                     ptr = pwd->pw_dir;
  365.                     while (*ptr) {
  366.                         mlout(*ptr);
  367.                         buf[cpos++] = *ptr++;
  368.                         ++ttcol;
  369.                     }
  370.                 }
  371.             }
  372. #endif
  373.             if (cpos == 0) {
  374.  
  375.                 /* output the home directory */
  376.                 ptr = home_ptr;
  377.                 while (*ptr) {
  378.                     mlout(*ptr);
  379.                     buf[cpos++] = *ptr++;
  380.                     ++ttcol;
  381.                 }
  382.  
  383.                 /* is this someone else's home directory */
  384.                 if (user_name[0] != 0) {
  385.  
  386.                     /* backup to the last directory sep */
  387.                     while ((cpos > 0) &&
  388.                            (buf[cpos-1] != DIRSEPCHAR)) {
  389.                         mlout('\b');
  390.                         mlout(' ');
  391.                         mlout('\b');
  392.                         --cpos;
  393.                         --ttcol;
  394.                     }
  395.  
  396.                     /* and add the user's name */
  397.                     ptr = user_name;
  398.                     while (*ptr) {
  399.                         mlout(*ptr);
  400.                         buf[cpos++] = *ptr++;
  401.                         ++ttcol;
  402.                     }
  403.                 }
  404.  
  405.             }
  406.  
  407.             /* and the last directory seperator */
  408.             if (buf[cpos-1] != DIRSEPCHAR) {
  409.                 mlout(DIRSEPCHAR);
  410.                 buf[cpos++] = DIRSEPCHAR;
  411.                 ++ttcol;
  412.             }
  413.             TTflush();
  414.  
  415.         } else if ((cpos > 1) &&
  416.                ((char)c == DIRSEPCHAR) &&
  417.                (type == CMP_FILENAME) &&
  418.                (buf[0] == '$')) {
  419.  
  420.             /* expand an environment variable reference */
  421.             /* save the variable name! */
  422.             buf[cpos] = 0;
  423.             strcpy(user_name, &buf[1]);
  424. #if    MSDOS | OS2 | VMS
  425.             mkupper(user_name);
  426. #endif
  427.  
  428.             /* erase the chars on-screen */
  429.             while (cpos > 0) {
  430.                 mlout('\b');
  431.                 mlout(' ');
  432.                 mlout('\b');
  433.                 --cpos;
  434.                 --ttcol;
  435.             }
  436.  
  437.             ptr = getenv(user_name);
  438.             if (ptr != (char *)NULL) {
  439.                 while (*ptr) {
  440.                     mlout(*ptr);
  441.                     buf[cpos++] = *ptr++;
  442.                     ++ttcol;
  443.                 }
  444.             }
  445.  
  446.             /* and the last directory seperator */
  447.             if (buf[cpos-1] != DIRSEPCHAR) {
  448.                 mlout(DIRSEPCHAR);
  449.                 buf[cpos++] = DIRSEPCHAR;
  450.                 ++ttcol;
  451.             }
  452.             TTflush();
  453.  
  454. #endif    /* ENVFUNC */
  455.  
  456.         } else if (c == '?') {
  457.  
  458. clist:            /* make a completion list! */
  459.             switch (type) {
  460.                 case CMP_BUFFER:
  461.                     clist_buffer(buf, &cpos);
  462.                     break;
  463.                 case CMP_COMMAND:
  464.                     clist_command(buf, &cpos);
  465.                     break;
  466. #if    !WINDOW_MSWIN
  467.                 case CMP_FILENAME:
  468.                     clist_file(buf, &cpos);
  469.                     break;
  470. #endif
  471.             }
  472.             update(TRUE);
  473.  
  474.             /* if it exists, reprompt the user */
  475.             if (prompt) {
  476.                 buf[cpos] = 0;
  477.                 if (type == CMP_COMMAND)
  478.                     mlwrite("%s%s", prompt, buf);
  479.                 else if (defval)
  480.                     mlwrite("%s[%s]: %s", prompt, defval, buf);
  481.                 else
  482.                     mlwrite("%s: %s", prompt, buf);
  483.             }
  484.  
  485.         } else {
  486.             if (cpos < maxlen && c > ' ') {
  487.                 buf[cpos++] = c;
  488.                 mlout(c);
  489.                 ++ttcol;
  490.                 TTflush();
  491.             }
  492.         }
  493.     }
  494. }
  495.  
  496. /*    comp_command:    Attempt a completion on a command name    */
  497.  
  498. comp_command(name, cpos)
  499.  
  500. char *name;    /* command containing the current name to complete */
  501. int *cpos;    /* ptr to position of next character to insert */
  502.  
  503. {
  504.     register NBIND *bp;    /* trial command to complete */
  505.     register int index;    /* index into strings to compare */
  506.     register int curbind;    /* index into the names[] array */
  507.     register NBIND *match;    /* last command that matches string */
  508.     register int matchflag;    /* did this command name match? */
  509.     register int comflag;    /* was there a completion at all? */
  510.  
  511.     /* start attempting completions, one character at a time */
  512.     comflag = FALSE;
  513.     curbind = 0;
  514.     while (*cpos < NSTRING) {
  515.  
  516.         /* first, we start at the first command and scan the list */
  517.         match = NULL;
  518.         curbind = 0;
  519.         while (curbind <= numfunc) {
  520.  
  521.             /* is this a match? */
  522.             bp = &names[curbind];
  523.             matchflag = TRUE;
  524.             for (index = 0; index < *cpos; index++)
  525.                 if (name[index] != bp->n_name[index]) {
  526.                     matchflag = FALSE;
  527.                     break;
  528.                 }
  529.  
  530.             /* if it is a match */
  531.             if (matchflag) {
  532.  
  533.                 /* if this is the first match, simply record it */
  534.                 if (match == NULL) {
  535.                     match = bp;
  536.                     name[*cpos] = bp->n_name[*cpos];
  537.                 } else {
  538.                     /* if there's a difference, stop here */
  539.                     if (name[*cpos] != bp->n_name[*cpos])
  540.                         return;
  541.                 }
  542.             }
  543.  
  544.             /* on to the next command */
  545.             curbind++;
  546.         }
  547.  
  548.         /* with no match, we are done */
  549.         if (match == NULL) {
  550.             /* beep if we never matched */
  551.             if (comflag == FALSE)
  552.                 TTbeep();
  553.             return;
  554.         }
  555.  
  556.         /* if we have completed all the way... go back */
  557.         if (name[*cpos] == 0) {
  558.             (*cpos)++;
  559.             return;
  560.         }
  561.  
  562.         /* remember we matched, and complete one character */
  563.         comflag = TRUE;
  564.         TTputc(name[(*cpos)++]);
  565.         TTflush();
  566.     }
  567.  
  568.     /* don't allow a completion past the end of the max command name length */
  569.     return;
  570. }
  571.  
  572. /*    clist_command:    Make a completion list based on a partial name */
  573.  
  574. clist_command(name, cpos)
  575.  
  576. char *name;    /* command containing the current name to complete */
  577. int *cpos;    /* ptr to position of next character to insert */
  578.  
  579. {
  580.     register NBIND *bp;    /* trial command to complete */
  581.     register int curbind;    /* index into the names[] array */
  582.     register int name_len;    /* current length of input string */
  583.     register BUFFER *listbuf;/* buffer to put completion list into */
  584.  
  585.     /* get a buffer for the completion list */
  586.     listbuf = bfind("[Completion list]", TRUE, BFINVS);
  587.     if (listbuf == NULL || bclear(listbuf) == FALSE) {
  588.         ctrlg(FALSE, 0);
  589.         TTflush();
  590.         return;
  591.     }
  592.  
  593.     name_len = *cpos;
  594.  
  595.     /* first, we start at the first command and scan the list */
  596.     for (curbind = 0; curbind <= numfunc; curbind++) {
  597.  
  598.         /* is this a match? */
  599.         bp = &names[curbind];
  600.         if (strncmp(name, bp->n_name, name_len) == 0)
  601.             addline(listbuf, bp->n_name);
  602.     }
  603.  
  604.     wpopup(listbuf);
  605.     return;
  606. }
  607.  
  608. /*    comp_buffer:    Attempt a completion on a buffer name    */
  609.  
  610. comp_buffer(name, cpos)
  611.  
  612. char *name;    /* buffer containing the current name to complete */
  613. int *cpos;    /* ptr to position of next character to insert */
  614.  
  615. {
  616.     register BUFFER *bp;    /* trial buffer to complete */
  617.     register int index;    /* index into strings to compare */
  618.     register BUFFER *match;    /* last buffer that matches string */
  619.     register int matchflag;    /* did this buffer name match? */
  620.     register int comflag;    /* was there a completion at all? */
  621.  
  622.     /* start attempting completions, one character at a time */
  623.     comflag = FALSE;
  624.     while (*cpos < NBUFN) {
  625.  
  626.         /* first, we start at the first buffer and scan the list */
  627.         match = NULL;
  628.         bp = bheadp;
  629.         while (bp) {
  630.  
  631.             /* is this a match? */
  632.             matchflag = TRUE;
  633.             for (index = 0; index < *cpos; index++)
  634.                 if (name[index] != bp->b_bname[index]) {
  635.                     matchflag = FALSE;
  636.                     break;
  637.                 }
  638.  
  639.             /* if it is a match */
  640.             if (matchflag) {
  641.  
  642.                 /* if this is the first match, simply record it */
  643.                 if (match == NULL) {
  644.                     match = bp;
  645.                     name[*cpos] = bp->b_bname[*cpos];
  646.                 } else {
  647.                     /* if there's a difference, stop here */
  648.                     if (name[*cpos] != bp->b_bname[*cpos])
  649.                         return;
  650.                 }
  651.             }
  652.  
  653.             /* on to the next buffer */
  654.             bp = bp->b_bufp;
  655.         }
  656.  
  657.         /* with no match, we are done */
  658.         if (match == NULL) {
  659.             /* beep if we never matched */
  660.             if (comflag == FALSE)
  661.                 TTbeep();
  662.             return;
  663.         }
  664.  
  665.         /* if we have completed all the way... go back */
  666.         if (name[*cpos] == 0) {
  667.             (*cpos)++;
  668.             return;
  669.         }
  670.  
  671.         /* remember we matched, and complete one character */
  672.         comflag = TRUE;
  673.         TTputc(name[(*cpos)++]);
  674.         TTflush();
  675.     }
  676.  
  677.     /* don't allow a completion past the end of the max buffer name length */
  678.     return;
  679. }
  680.  
  681. /*    clist_buffer:    Make a completion list based on a partial buffer name */
  682.  
  683. clist_buffer(name, cpos)
  684.  
  685. char *name;    /* command containing the current name to complete */
  686. int *cpos;    /* ptr to position of next character to insert */
  687.  
  688. {
  689.     register int name_len;    /* current length of input string */
  690.     register BUFFER *listbuf;/* buffer to put completion list into */
  691.     register BUFFER *bp;    /* trial buffer to complete */
  692.  
  693.     /* get a buffer for the completion list */
  694.     listbuf = bfind("[Completion list]", TRUE, BFINVS);
  695.     if (listbuf == NULL || bclear(listbuf) == FALSE) {
  696.         ctrlg(FALSE, 0);
  697.         TTflush();
  698.         return;
  699.     }
  700.  
  701.     /* first, we start at the first buffer and scan the list */
  702.     name_len = *cpos;
  703.     bp = bheadp;
  704.  
  705.     while (bp) {
  706.  
  707.         /* is this a match? */
  708.         if (strncmp(name, bp->b_bname, name_len) == 0)
  709.             addline(listbuf, bp->b_bname);
  710.  
  711.         /* on to the next buffer */
  712.         bp = bp->b_bufp;
  713.     }
  714.  
  715.     wpopup(listbuf);
  716.     return;
  717. }
  718.  
  719. #if    !WINDOW_MSWIN
  720. /*    comp_file:    Attempt a completion on a file name    */
  721.  
  722. comp_file(name, cpos)
  723.  
  724. char *name;    /* file containing the current name to complete */
  725. int *cpos;    /* ptr to position of next character to insert */
  726.  
  727. {
  728.     register char *fname;    /* trial file to complete */
  729.     register int index;    /* index into strings to compare */
  730.  
  731.     register int matches;    /* number of matches for name */
  732.     char longestmatch[NSTRING]; /* temp buffer for longest match */
  733.     int longestlen;     /* length of longest match (always > *cpos) */
  734.  
  735.     /* everything (or nothing) matches an empty string */
  736.     if (*cpos == 0)
  737.         return;
  738.  
  739.     /* first, we start at the first file and scan the list */
  740.     matches = 0;
  741.     name[*cpos] = 0;
  742.     fname = getffile(name);
  743.     while (fname) {
  744.  
  745.         /* is this a match? */
  746.         if (strncmp(name,fname,*cpos) == 0) {
  747.  
  748.             /* count the number of matches */
  749.             matches++;
  750.  
  751.             /* if this is the first match, simply record it */
  752.             if (matches == 1) {
  753.                 strcpy(longestmatch,fname);
  754.                 longestlen = strlen(longestmatch);
  755.             } else {
  756.  
  757.                 /* if there's a difference, stop here */
  758.                 if (longestmatch[*cpos] != fname[*cpos])
  759.                     return;
  760.  
  761.                 for (index = (*cpos) + 1; index < longestlen; index++)
  762.                     if (longestmatch[index] != fname[index]) {
  763.                         longestlen = index;
  764.                         longestmatch[longestlen] = 0;
  765.                     }
  766.             }
  767.         }
  768.  
  769.         /* on to the next file */
  770.         fname = getnfile();
  771.     }
  772.  
  773.     /* beep if we never matched */
  774.     if (matches == 0) {
  775.         TTbeep();
  776.         return;
  777.     }
  778.  
  779.     /* the longestmatch array contains the longest match so copy and print it */
  780.     for ( ; (*cpos < (NSTRING-1)) && (*cpos < longestlen); (*cpos)++) {
  781.         name[*cpos] = longestmatch[*cpos];
  782.         TTputc(name[*cpos]);
  783.     }
  784.  
  785.     name[*cpos] = 0;
  786.  
  787.     /* if only one file matched then increment cpos to signal complete() */
  788.     /* that this was a complete match.  If a directory was matched then */
  789.     /* last character will be the DIRSEPCHAR.  In this case we do NOT */
  790.     /* want to signal a complete match. */
  791.     if ((matches == 1) && (name[(*cpos)-1] != DIRSEPCHAR))
  792.         (*cpos)++;
  793.  
  794.     TTflush();
  795.  
  796.     return;
  797. }
  798.  
  799. /*    clist_file:    Make a completion list based on a partial file name */
  800.  
  801. clist_file(name, cpos)
  802.  
  803. char *name;    /* command containing the current name to complete */
  804. int *cpos;    /* ptr to position of next character to insert */
  805.  
  806. {
  807.     register int name_len;    /* current length of input string */
  808.     register BUFFER *listbuf;/* buffer to put completion list into */
  809.     register char *fname;    /* trial file to complete */
  810.  
  811.     /* get a buffer for the completion list */
  812.     listbuf = bfind("[Completion list]", TRUE, BFINVS);
  813.     if (listbuf == NULL || bclear(listbuf) == FALSE) {
  814.         ctrlg(FALSE, 0);
  815.         TTflush();
  816.         return;
  817.     }
  818.  
  819.     /* first, we start at the first file and scan the list */
  820.     name_len = *cpos;
  821.     name[*cpos] = 0;
  822.     fname = getffile(name);
  823.  
  824.     /* first, we start at the first file and scan the list */
  825.     while (fname) {
  826.  
  827.         /* is this a match? */
  828.         if (strncmp(name, fname, name_len) == 0)
  829.             addline(listbuf, fname);
  830.  
  831.         /* on to the next file */
  832.         fname = getnfile();
  833.     }
  834.  
  835.     wpopup(listbuf);
  836.     return;
  837. }
  838. #endif
  839.  
  840. /*    tgetc:    Get a key from the terminal driver, resolve any keyboard
  841.         macro action                    */
  842.  
  843. int PASCAL NEAR tgetc()
  844.  
  845. {
  846.     int c;    /* fetched character */
  847.  
  848.     /* if we are playing a keyboard macro back, */
  849.     if (kbdmode == PLAY) {
  850.  
  851.         /* if there is some left... */
  852.         if (kbdptr < kbdend)
  853.             return((int)*kbdptr++);
  854.  
  855.         /* at the end of last repitition? */
  856.         if (--kbdrep < 1) {
  857.             kbdmode = STOP;
  858. #if    VISMAC == 0
  859.             /* force a screen update after all is done */
  860.             update(FALSE);
  861. #endif
  862.         } else {
  863.  
  864.             /* reset the macro to the begining for the next rep */
  865.             kbdptr = &kbdm[0];
  866.             return((int)*kbdptr++);
  867.         }
  868.     }
  869.  
  870.     /* if no pending character */
  871.     if (cpending == FALSE) {
  872.  
  873.         /* fetch a character from the terminal driver */
  874.         c = TTgetc();
  875.  
  876.     } else {
  877.         
  878.         c = charpending;
  879.         cpending = FALSE;
  880.     }
  881.  
  882.     /* record it for $lastkey */
  883.     lastkey = c;
  884.  
  885.     /* save it if we need to */
  886.     if (kbdmode == RECORD) {
  887.         *kbdptr++ = c;
  888.         kbdend = kbdptr;
  889.  
  890.         /* don't overrun the buffer */
  891.         if (kbdptr == &kbdm[NKBDM - 1]) {
  892.             kbdmode = STOP;
  893.             TTbeep();
  894.         }
  895.     }
  896.  
  897.     /* and finally give the char back */
  898.     return(c);
  899. }
  900.  
  901. /*    getkey:    Get one keystroke. The legal prefixs here
  902.             are the SPEC, MOUS and CTRL prefixes.
  903. */
  904.  
  905. PASCAL NEAR getkey()
  906.  
  907. {
  908.     int c;        /* next input character */
  909.     int upper;    /* upper byte of the extended sequence */
  910.  
  911.     /* get a keystroke */
  912.         c = tgetc();
  913.  
  914.     /* if it exists, process an escape sequence */
  915.     if (c == 0) {
  916.  
  917.         /* get the event type */
  918.         upper = tgetc();
  919.  
  920.         /* mouse events need us to read in the row/col */
  921.         if (upper & (MOUS >> 8)) {
  922.             /* grab the x/y position of the mouse */
  923.             xpos = tgetc();
  924.             ypos = tgetc();
  925.         }
  926.  
  927.         /* get the event code */
  928.         c = tgetc();
  929.  
  930.         /* if it is a function key... map it */
  931.         c = (upper << 8) | c;
  932.     }
  933.  
  934.     /* yank out the control prefix */
  935.         if (((c & 255) >=0x00 && (c & 255) <= 0x1F) || (c & 255) == 0x7F)
  936.                 c = CTRL | (c ^ 0x40);
  937.  
  938.     /* return the character */
  939.         return(c);
  940. }
  941.  
  942. /*    GETCMD:    Get a command from the keyboard. Process all applicable
  943.         prefix keys
  944.                             */
  945. PASCAL NEAR getcmd()
  946.  
  947. {
  948.     int c;        /* fetched keystroke */
  949.     KEYTAB *key;    /* ptr to a key entry */
  950.  
  951.     /* get initial character */
  952.     c = getkey();
  953.     key = getbind(c);
  954.  
  955.     /* resolve META and CTLX prefixes */
  956.     if (key) {
  957.         if (key->k_ptr.fp == meta) {
  958.             c = getkey();
  959. #if    SMOS
  960.             c = upperc(c&255) | (c & ~255); /* Force to upper */
  961. #else
  962.             c = upperc(c) | (c & ~255);    /* Force to upper */
  963. #endif
  964.             c |= META;
  965.         } else if (key->k_ptr.fp == cex) {
  966.             c = getkey();
  967. #if    SMOS
  968.             c = upperc(c&255) | (c & ~255); /* Force to upper */
  969. #else
  970.             c = upperc(c) | (c & ~255);    /* Force to upper */
  971. #endif
  972.             c |= CTLX;
  973.         }
  974.     }
  975.  
  976.     /* return it */
  977.     return(c);
  978. }
  979.  
  980. /*    A more generalized prompt/reply function allowing the caller
  981.     to specify the proper terminator. If the terminator is not
  982.     a return('\r'), return will echo as "<NL>"
  983.                             */
  984. int PASCAL NEAR getstring(buf, nbuf, eolchar)
  985.  
  986. char *buf;
  987. int nbuf;
  988. int eolchar;
  989.  
  990. {
  991.     register int cpos;    /* current character position in string */
  992.     register int c;        /* current input character */
  993.     register int ec;    /* extended current input character */
  994.     register int quotef;    /* are we quoting the next char? */
  995.     char *kp;        /* pointer into key_name */
  996.     char key_name[10];    /* name of a quoted key */
  997.  
  998.     cpos = 0;
  999.     quotef = FALSE;
  1000.  
  1001.     for (;;) {
  1002.         /* get a character from the user */
  1003.         ec = getkey();
  1004.  
  1005.         /* if they hit the line terminate, wrap it up */
  1006.         if (ec == eolchar && quotef == FALSE) {
  1007.             buf[cpos++] = 0;
  1008.  
  1009.             /* clear the message line */
  1010.             mlerase();
  1011.  
  1012.             /* if we default the buffer, return FALSE */
  1013.             if (buf[0] == 0)
  1014.                 return(FALSE);
  1015.  
  1016.             return(TRUE);
  1017.         }
  1018.  
  1019.         /* change from command form back to character form */
  1020.         c = ectoc(ec);
  1021.  
  1022.         if ((ec == abortc) && (quotef == FALSE)) {
  1023.             /* Abort the input? */
  1024.             ctrlg(FALSE, 0);
  1025.             TTflush();
  1026.             return(ABORT);
  1027.         }
  1028.  
  1029.         /* if it is from the mouse, or is a function key, blow it off */
  1030.         if ((quotef == FALSE) && ((ec & MOUS) || (ec & SPEC)))
  1031.             continue;
  1032.  
  1033.         /* rubout/erase */
  1034.         if ((c==0x7F || c==0x08) && quotef==FALSE) {
  1035.             if (cpos != 0) {
  1036.                 outstring("\b \b");
  1037.                 --ttcol;
  1038.  
  1039.                 if (buf[--cpos] < 0x20) {
  1040.                     outstring("\b \b");
  1041.                     --ttcol;
  1042.                 }
  1043.  
  1044.                 if (buf[cpos] == '\r') {
  1045.                     outstring("\b\b  \b\b");
  1046.                     ttcol -= 2;
  1047.                 }
  1048.                 TTflush();
  1049.             }
  1050.             continue;
  1051.          }
  1052.  
  1053.         /* C-K, kill default buffer and return null */
  1054.         if (c == 0x0b && quotef == FALSE) {
  1055.  
  1056.              /* clear the buffer */
  1057.              buf[0] = 0;
  1058.  
  1059.              /* clear the message line and return */
  1060.              mlwrite("");
  1061.              TTflush();
  1062.              return(TRUE);
  1063.         }
  1064.  
  1065.         /* C-U, kill */
  1066.         if (c == 0x15 && quotef == FALSE) {
  1067.  
  1068.             while (cpos != 0) {
  1069.                 outstring("\b \b");
  1070.                 --ttcol;
  1071.  
  1072.                 if (buf[--cpos] < 0x20) {
  1073.                     outstring("\b \b");
  1074.                     --ttcol;
  1075.                 }
  1076.                 if (buf[cpos] == '\r') {
  1077.                     outstring("\b\b  \b\b");
  1078.                     ttcol -= 2;
  1079.                 }
  1080.             }
  1081.             TTflush();
  1082.             continue;
  1083.         }
  1084.  
  1085.         /* quoting next character? */
  1086.         if ((ec == quotec) && (quotef == FALSE)) {
  1087.             quotef = TRUE;
  1088.             continue;
  1089.         }
  1090.  
  1091.         quotef = FALSE;
  1092.  
  1093.         /* if it is from the mouse, or is a function key,
  1094.            insert it's name since it was quoted */
  1095.         if ((ec & MOUS) || (ec & SPEC)) {
  1096.             cmdstr(ec, key_name);
  1097.             kp = key_name;
  1098.             while (*kp) {
  1099.                 if (cpos < nbuf - 1) {
  1100.                     if (disinp)
  1101.                         mlout(*kp);
  1102.                     buf[cpos++] = *kp++;
  1103.                     ++ttcol;
  1104.                 }
  1105.             }
  1106.             TTflush();
  1107.             continue;
  1108.         }
  1109.  
  1110.         /* insert the character in the string! */
  1111.         if (cpos < nbuf-1) {
  1112.  
  1113.             buf[cpos++] = c;
  1114.  
  1115.             if ((c < ' ') && (c != '\r')) {
  1116.                 outstring("^");
  1117.                 ++ttcol;
  1118.                 c ^= 0x40;
  1119.             }
  1120.  
  1121.             if (c != '\r') {
  1122.                 if (disinp)
  1123.                     mlout(c);
  1124.             } else {    /* put out <NL> for <ret> */
  1125.                 outstring("<NL>");
  1126.                 ttcol += 3;
  1127.             }
  1128.             ++ttcol;
  1129.             TTflush();
  1130.         }
  1131.     }
  1132. }
  1133.  
  1134. PASCAL NEAR outstring(s) /* output a string of input characters */
  1135.  
  1136. char *s;    /* string to output */
  1137.  
  1138. {
  1139.     if (disinp)
  1140.         while (*s)
  1141.             mlout(*s++);
  1142. }
  1143.  
  1144. PASCAL NEAR ostring(s)    /* output a string of output characters */
  1145.  
  1146. char *s;    /* string to output */
  1147.  
  1148. {
  1149.     if (discmd)
  1150.         while (*s)
  1151.             mlout(*s++);
  1152. }
  1153.  
  1154. /*
  1155.  * mlprompt -- Display a prompt [with optional default] and the
  1156.  *    input terminator.
  1157.  */
  1158. int PASCAL NEAR mlprompt(prompt, dflt, iterm)
  1159. char *prompt;
  1160. char *dflt;
  1161. int iterm;
  1162. {
  1163.     register int tcol;
  1164.     char buf[NSTRING];
  1165.  
  1166.     /* don't bother displaying if we are't currently */
  1167.     if (discmd == FALSE)
  1168.         return(0);
  1169.  
  1170.     /* show the passed in prompt */
  1171.     mlwrite(prompt);
  1172.     tcol = strlen(prompt);
  1173.  
  1174.     /* If there's a default, put it in brackets and show it. */
  1175.     if (dflt != NULL && *dflt != '\0') {
  1176.         mlout('[');
  1177.         tcol = 1 + echostring(dflt, tcol + 1, NPAT/2);
  1178.         mlout(']');
  1179.     }
  1180.  
  1181.     /* Display the proper current search terminator character. */
  1182.     mlout('<');
  1183.     switch (iterm) {
  1184.         case CTRL | '[':
  1185.             mlputs("META"); tcol += 8; break;
  1186.         case CTRL | 'M':
  1187.             mlputs("NL"); tcol += 6; break;
  1188.         default:
  1189.             mlputs(cmdstr(iterm, buf));
  1190.             tcol += strlen(buf) + 4;
  1191.     }
  1192.     mlputs(">: ");
  1193.     TTflush();
  1194.     return(tcol);
  1195. }
  1196.  
  1197. /*
  1198.  * echostring -- Use echochar() to put out a string.  Checks for NULL.
  1199.  */
  1200. int PASCAL NEAR echostring(str, tcol, uptocol)
  1201. char *str;    /* characters to be echoed */
  1202. int tcol;    /* column to be echoed in */
  1203. int uptocol;    /* last column to be echoed in */
  1204. {
  1205.     if (str != NULL) {
  1206.         while (*str) {
  1207.             tcol = echochar(*str++, tcol);
  1208.             if (tcol >= uptocol) {
  1209.                 mlout('$');
  1210.                 tcol++;
  1211.                 break;
  1212.             }
  1213.         }
  1214.     }
  1215.     return(tcol);
  1216. }
  1217.  
  1218. /*
  1219.  * Routine to echo i-search and message-prompting characters.
  1220.  */
  1221. int PASCAL NEAR echochar(c, col)
  1222.  
  1223. unsigned char c;    /* character to be echoed */
  1224. int col;        /* column to be echoed in */
  1225.  
  1226. {
  1227.     movecursor(term.t_nrow, col);    /* Position the cursor    */
  1228.     if (c == '\r') {        /* Newline character?    */
  1229.         mlout('<');
  1230.         mlout('N');
  1231.         mlout('L');
  1232.         mlout('>');
  1233.         col += 3;
  1234.     }
  1235. #if 0
  1236.     else if (c == '\t') {        /* Tab character?    */
  1237.         mlout('<');
  1238.         mlout('T');
  1239.         mlout('A');
  1240.         mlout('B');
  1241.         mlout('>');
  1242.         col += 4;
  1243.     }
  1244. #endif
  1245.     else if ((c < ' ') || (c == 0x7F)) {    /* Vanilla control char and Rubout:   */
  1246.         mlout('^');     /* Yes, output prefix        */
  1247.         mlout(c ^ 0x40);/* Make it "^X"            */
  1248.         col++;        /* Count this char        */
  1249.     }
  1250.     else
  1251.         mlout(c);        /* Otherwise, output raw char    */
  1252.     TTflush();            /* Flush the output        */
  1253.     return(++col);            /* return the new column number */
  1254. }
  1255.